﻿Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports System.Threading

Namespace System.Workflow.Runtime
    Friend NotInheritable Class InstanceLock
        ' Methods
        Friend Sub New(ByVal id As Guid, ByVal name As String, ByVal priority As Integer, ByVal lockOperator As LockPriorityOperator)
            Me.m_instanceId = id
            Me.m_name = name
            Me.m_priority = priority
            Me.m_operator = lockOperator
        End Sub

        <Conditional("DEBUG")> _
        Friend Shared Sub AssertIsLocked(ByVal theLock As InstanceLock)
        End Sub

        <Conditional("DEBUG")> _
        Friend Shared Sub AssertNoLocksHeld()
        End Sub

        Friend Function Enter() As InstanceLockGuard
            Return New InstanceLockGuard(Me)
        End Function

        Friend Sub [Exit]()
            Try 
                InstanceLock.HeldLocks.Remove(Me)
            Finally
                Monitor.Exit(Me)
            End Try
        End Sub

        Friend Function TryEnter() As Boolean
            InstanceLockGuard.EnforceGuard(Me)
            If Not Monitor.TryEnter(Me) Then
                Return False
            End If
            Try 
                InstanceLock.HeldLocks.Add(Me)
            Catch obj1 As Object
                Monitor.Exit(Me)
                Return False
            End Try
            Return True
        End Function


        ' Properties
        Private Shared ReadOnly Property HeldLocks As List([Of] InstanceLock)
            Get
                Dim list As List([Of] InstanceLock) = InstanceLock.t_heldLocks
                If (list Is Nothing) Then
                    InstanceLock.t_heldLocks = New List([Of] InstanceLock)
                    list = InstanceLock.t_heldLocks
                End If
                Return list
            End Get
        End Property

        Friend ReadOnly Property InstanceId As Guid
            Get
                Return Me.m_instanceId
            End Get
        End Property

        Friend ReadOnly Property [Operator] As LockPriorityOperator
            Get
                Return Me.m_operator
            End Get
        End Property

        Friend ReadOnly Property Priority As Integer
            Get
                Return Me.m_priority
            End Get
        End Property


        ' Fields
        Private m_instanceId As Guid
        Private m_name As String
        Private m_operator As LockPriorityOperator
        Private m_priority As Integer
        <ThreadStatic> _
        Private Shared t_heldLocks As List([Of] InstanceLock)

        ' Nested Types
        <StructLayout(LayoutKind.Sequential)> _
        Friend Structure InstanceLockGuard
            Implements IDisposable
            Private m_lock As InstanceLock
            Friend Shared Sub EnforceGuard(ByVal theLock As InstanceLock)
                Dim lock As InstanceLock
                For Each lock In InstanceLock.HeldLocks
                    Select Case theLock.[Operator]
                        Case LockPriorityOperator.GreaterThan
                            If ((lock.InstanceId = theLock.InstanceId) AndAlso (lock.Priority <= theLock.Priority)) Then
                                Throw New InvalidOperationException(ExecutionStringManager.InstanceOperationNotValidinWorkflowThread)
                            End If
                            [Continue]
                        Case LockPriorityOperator.GreaterThanOrReentrant
                            If ((lock.InstanceId = theLock.InstanceId) AndAlso (lock.Priority < theLock.Priority)) Then
                                Throw New InvalidOperationException(ExecutionStringManager.InstanceOperationNotValidinWorkflowThread)
                            End If
                            [Continue]
                    End Select
                Next
            End Sub

            Friend Sub New(ByVal theLock As InstanceLock)
                Me.m_lock = theLock
                InstanceLockGuard.EnforceGuard(theLock)
                InstanceLock.HeldLocks.Add(Me.m_lock)
                Monitor.Enter(Me.m_lock)
            End Sub

            Friend Sub Pulse()
                Monitor.Pulse(Me.m_lock)
            End Sub

            Friend Sub Wait()
                Monitor.Wait(Me.m_lock)
            End Sub

            Public Sub Dispose()
                InstanceLock.HeldLocks.Remove(Me.m_lock)
                Monitor.Exit(Me.m_lock)
            End Sub
        End Structure
    End Class
End Namespace

